package message_controller

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"shimmer/neusoft_club_backend/controllers/cons"
	"shimmer/neusoft_club_backend/controllers/utils"
	"shimmer/neusoft_club_backend/dao/domain"
	"shimmer/neusoft_club_backend/middlewares/auth_middleware"
	"shimmer/neusoft_club_backend/services"
	"shimmer/neusoft_club_backend/services/club_service"
	"shimmer/neusoft_club_backend/services/errs"
	"shimmer/neusoft_club_backend/services/message_service"
)

type getSelfMessageReq struct {
	Type     *domain.MessageType `json:"type" form:"type"`
	IsMarked *bool               `json:"is_marked" form:"is_marked"`
}
type getSelfMessageInClubReq struct {
	Type     *domain.MessageType `json:"type" form:"type"`
	IsMarked *bool               `json:"is_marked" form:"is_marked"`
}

// @Summary 获取自己发出去的消息
// @Description
// @Tags 消息,用户
// @Param page query integer false "分页"
// @Param size query integer true "size"
// @Param type query integer false "消息类型"
// @Success 200 {object} []domain.MessageDTO
// @Router /api/message/self/sent [get]
func getSelfSentMessages(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	pageQuery, err := utils.GetPaginationFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	req := &getSelfMessageReq{}
	if err = c.ShouldBind(req); err != nil {
		services.AbortWithError(c, err)
		return
	}
	where := map[string]interface{}{}
	if req.Type != nil {
		where["type"] = *req.Type
	}
	res, err := message_service.FindAllMessagesOfUserSent(user.ID, where, pageQuery)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)
}

// @Summary 获取我收到的消息
// @Description
// @Tags 消息,用户
// @Param page query integer false "分页"
// @Param size query integer true "size"
// @Param type query integer false "消息类型"
// @Param is_marked query boolean false "是否已读"
// @Success 200 {object} []domain.MessageDTO
// @Router /api/message/self/received [get]
func getSelfReceivedMessage(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	pageQuery, err := utils.GetPaginationFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	req := &getSelfMessageReq{}
	if err = c.ShouldBind(req); err != nil {
		services.AbortWithError(c, err)
		return
	}
	where := map[string]interface{}{}
	if req.IsMarked != nil {
		where["mark_read"] = *req.IsMarked
	}
	if req.Type != nil {
		where["type"] = *req.Type
	}
	res, err := message_service.FindAllMessagesOfUserReceived(user.ID, where, pageQuery)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)
}

// @Summary 获取自己所加入社团的消息
// @Description
// @Tags 消息
// @Param page query integer false "分页"
// @Param size query integer true  "size"
// @Param is_marked query boolean false "是否已读"
// @Success 200 {object} []domain.MessageDTO
// @Router /api/message/self/club [get]
func getSelfClubMessage(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	pageQuery, err := utils.GetPaginationFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}

	where := map[string]interface{}{}
	req := &getSelfMessageInClubReq{}
	if err = c.ShouldBind(req); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if req.IsMarked != nil {
		where["mark_read"] = *req.IsMarked
		where["receiver_id"] = user.ID
	}
	res, err := message_service.FindAllMessagesOfClubThatUsersJoin(user.ID, where, pageQuery)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)
}

// @Summary 获取社团的消息
// @Description
// @Tags 消息
// @Param clubID path integer true "社团ID"
// @Param page query integer false "分页"
// @Param size query integer true  "size"
// @Param is_marked query boolean false "是否已读"
// @Success 200 {object} []domain.MessageDTO
// @Router /api/message/club/{clubID} [get]
func getClubMessage(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	pageQuery, err := utils.GetPaginationFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	inClub, err := club_service.CheckInClub(user.ID, clubBinding.ClubID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !inClub {
		services.AbortWithError(c, errs.ErrIsNotClubMember)
		return
	}
	where := map[string]interface{}{}
	req := &getSelfMessageInClubReq{}
	if err = c.ShouldBind(req); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if req.IsMarked != nil {
		where["mark_read"] = *req.IsMarked
		where["receiver_id"] = user.ID
	}
	res, err := message_service.FindAllMessagesOfClub(clubBinding.ClubID, where, pageQuery)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)
}

// @Summary 标记消息为已读
// @Description
// @Tags 消息
// @Param message_id path integer true "消息ID"
// @Success 204
// @Router /api/message/single/{message_id}/read [post]
func markMessageAsRead(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	messageBinding := &cons.MessageUriParamBinding{}

	if err = c.ShouldBindUri(messageBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}

	message, err := message_service.FindMessageByID(messageBinding.MessageID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	isAdmin, err := club_service.CheckIsAdmin(user.ID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if message.ReceiverID == nil {
		if message.Type == domain.MessageTypeClub {
			if message.ClubID == nil && !isAdmin {
				services.AbortWithError(c, errs.ErrIsBrokenMessage)
				return
			}
			inClub, err := club_service.CheckInClub(user.ID, *message.ClubID)
			if err != nil {
				services.AbortWithError(c, err)
				return
			}
			if !inClub {
				services.AbortWithError(c, errs.ErrIsNotClubMember)
				return
			}
		}
	}
	if message.ReceiverID != nil && *message.ReceiverID != user.ID {
		services.AbortWithError(c, errs.ErrIsNotMessageReceiver)
		return
	}
	err = message_service.MarkMessageAsRead(messageBinding.MessageID, user.ID)

	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.AbortWithStatus(http.StatusNoContent)
}

type SendReq struct {
	Title   string `json:"title" form:"title" query:"title" xml:"title" binding:"required"`
	Content string `json:"message,omitempty" form:"message" query:"message" xml:"message" binding:"required"`
}

// @Summary 发送消息
// @Description
// @Tags 消息
// @Param user_id path integer true "用户ID"
// @Param value body message_controller.SendReq true "value"
// @Success 200 {object} domain.MessageDTO
// @Router /api/message/user/{user_id} [post]
func sendToUser(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	userBinding := &cons.UserUriParamBinding{}

	if err = c.ShouldBindUri(userBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	sendReq := &SendReq{}
	if err = c.ShouldBind(sendReq); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if isAdmin, _ := club_service.CheckIsAdmin(user.ID); isAdmin {
		res, err := message_service.SendMessage(&message_service.SendMessageReq{
			Title:      sendReq.Title,
			Content:    sendReq.Content,
			ReceiverID: &userBinding.UserID,
			SenderID:   &user.ID,
		})
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		c.JSON(http.StatusOK, res)
	}
	canSend, err := message_service.CheckUserCanSend(user.ID, userBinding.UserID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !canSend {
		services.AbortWithError(c, errs.ErrCannotSendMessage)
		return
	}
	res, err := message_service.SendMessage(&message_service.SendMessageReq{
		Title:      sendReq.Title,
		Content:    sendReq.Content,
		ReceiverID: &userBinding.UserID,
		SenderID:   &user.ID,
		Type:       domain.MessageTypeSender,
	})
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)

}

// @Summary 发送消息到社团
// @Description
// @Tags 消息
// @Param club_id path integer true "社团ID"
// @Param value body message_controller.SendReq true "value"
// @Success 200 {object} domain.MessageDTO
// @Router /api/message/club/{club_id} [post]
func sendToClub(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}

	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	req := &SendReq{}
	if err = c.ShouldBind(req); err != nil {
		services.AbortWithError(c, err)
		return
	}
	owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !owner {
		services.AbortWithError(c, errs.ErrNotClubManager)
		return
	}
	res, err := message_service.SendMessage(&message_service.SendMessageReq{
		Title:      req.Title,
		Content:    req.Content,
		ReceiverID: nil,
		SenderID:   &user.ID,
		ClubID:     &clubBinding.ClubID,
		Type:       domain.MessageTypeClub,
		Extra:      nil,
	})
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, res)
}

// @Summary 获取消息详情
// @Description
// @Tags 消息
// @Param message_id path integer true "消息ID"
// @Success 200 {object} domain.MessageDTO
// @Router /api/message/single/{message_id} [get]
func messageDetail(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	messageBinding := &cons.MessageUriParamBinding{}
	if err = c.ShouldBindUri(messageBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	message, err := message_service.FindMessageByID(messageBinding.MessageID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if (message.SenderID != nil && *message.SenderID != user.ID) || message.ReceiverID == nil || *message.ReceiverID != user.ID {
		isAdmin, err := club_service.CheckIsAdmin(user.ID)
		if err != nil || !isAdmin {
			services.AbortWithError(c, errs.ErrCannotDetailForMessage)
			return
		}
	}
	c.JSON(http.StatusOK, message)
}

func ConfigureMessageController(r *gin.RouterGroup) {
	r.GET("/self/sent", auth_middleware.AuthLoginMiddleware(), getSelfSentMessages)
	r.GET("/self/received", auth_middleware.AuthLoginMiddleware(), getSelfReceivedMessage)
	r.GET("/self/club", auth_middleware.AuthLoginMiddleware(), getSelfClubMessage)
	r.GET(fmt.Sprintf("/single/:%s", cons.MessageParam), auth_middleware.AuthLoginMiddleware(), messageDetail)
	r.POST(fmt.Sprintf("/single/:%s/read", cons.MessageParam), auth_middleware.AuthLoginMiddleware(), markMessageAsRead)
	r.POST(fmt.Sprintf("/user/:%s", cons.UserUriParam), auth_middleware.AuthLoginMiddleware(), sendToUser)
	r.POST(fmt.Sprintf("/club/:%s", cons.ClubUriParam), auth_middleware.AuthLoginMiddleware(), sendToClub)
	r.GET(fmt.Sprintf("/club/:%s", cons.ClubUriParam), auth_middleware.AuthLoginMiddleware(), getClubMessage)
}
